<?php
/*
 * Copyright (c) 2011 Antoine d'Otreppe de Bouvette
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

class AspyctSQLiteDatabase implements Database {

    /**
     * PDO connection to the database
     * @var PDO
     */
    private $pdo;

    /**
     * Username to connect to the database
     * @var string
     */
    private $user;

    /**
     * Password to connect to the database
     * @var string
     */
    private $pass;

    /**
     * The table prefix
     * @var string
     */
    private $prefix;

    /**
     * The database init SQL
     * @var string
     */
    private $init;
    
    /**
     *
     * @var Logger
     */
    private $logger;

    /**
     * @param string $dsn A data source name as accepted by PDO
     * @param array $options An array of options for this database
     */
    public function __construct($dsn, array $options) {
        $this->dsn = $dsn;
        $this->user = array_get($options, 'user', null);
        $this->pass = array_get($options, 'pass', null);
        $this->prefix = array_get($options, 'prefix', '');
    }

    /**
     * Executes a query in the database, and returns the resulting PDOStatement
     * @param string $statement A SQL statement. The # symbol is replaced
     * with the table prefix.
     * @param mixed[] $varargs arguments for this query
     * @return PDOStatement
     */
    public function query($statement /* , $varargs */) {
        $args = func_get_args();
        array_shift($args);

        return $this->vquery($statement, $args);
    }

    /**
     * Executes a query in the database, and returns the resulting PDOStatement
     * @param string $statement
     * @param array $args
     * @return PDOStatement
     */
    public function vquery($statement, array $args) {
        $statement = str_replace('#', $this->prefix, $statement);

        foreach ($args as $i => $value) {
            if (is_array($value)) {
                // The array is not accepted by all PDO drivers
                $pdo = $this->getPdo();

                $all = array();
                foreach ($value as $v) {
                    $all[] = $pdo->quote($v);
                }

                $args[$i] = implode(', ', $all);
            }
        }

        try {
            $ps = $this->getPdo()->prepare($statement);
        }
        catch (PDOException $e) {
            $this->getLogger()->error($this, $e);
            throw new RuntimeException(
                    "Could not prepare statement, because of
                    {$e->getMessage()}. Statement was: $statement");
        }

        $ps->execute($args);

        return $ps;
    }
    
    /**
     * @return SQLiteSelect
     */
    public function createSelect() {
        return new SQLiteSelect();
    }

    /**
     * @return PDO
     */
    public function getPdo() {
        if ($this->pdo === null) {
            $this->pdo = new PDO($this->dsn, $this->user, $this->pass);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }

        return $this->pdo;
    }

    public function beginTransaction() {
        $this->pdo->beginTransaction();
    }

    public function commitTransaction() {
        $this->pdo->commit();
    }

    public function rollbackTransaction() {
        $this->pdo->rollBack();
    }

    /**
     * @param string $sequence
     * @return int
     */
    public function lastInsertId($sequence=null) {
        return $this->pdo->lastInsertId($sequence);
    }

    /**
     * @param string $filename 
     */
    public function initWithFile($filename) {
        $this->getPdo()->exec(file_get_contents($filename));
    }

    /**
     *
     * @return Logger
     */
    public function getLogger() {
        return $this->logger;
    }

    /**
     *
     * @param Logger $logger 
     */
    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }
}
